feat(ssd1327): Add blink animation example with framebuf scaling.#420
feat(ssd1327): Add blink animation example with framebuf scaling.#420MatteoCnda1 wants to merge 1 commit intomainfrom
Conversation
0c48ca6 to
b361f99
Compare
There was a problem hiding this comment.
Pull request overview
Adds a new SSD1327 driver example demonstrating a scaled bitmap blink animation using framebuf buffers and blit() on the STeaMi 128×128 OLED, intended to replace/clean up an example previously living in micropython-steami-sample (Issue #407).
Changes:
- Added
lib/ssd1327/examples/blink_animation.pyimplementing a multi-frame eye blink animation. - Uses
sleep_mstiming and atry/finallyto clear the display on exit. - Implements pixel-by-pixel scaling into an intermediate framebuffer prior to blitting.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| scaled_bitmap = bytearray((scaled_w * scaled_h) // 8) | ||
| scaled_buf = framebuf.FrameBuffer( | ||
| scaled_bitmap, scaled_w, scaled_h, framebuf.MONO_HLSB | ||
| ) | ||
|
|
||
| # Scale up pixel by pixel | ||
| for y in range(EYE_H): | ||
| for x in range(EYE_W): | ||
| if buf.pixel(x, y): | ||
| scaled_buf.fill_rect(x * SCALE, y * SCALE, SCALE, SCALE, 1) |
There was a problem hiding this comment.
display.framebuf is GS4_HMSB (4-bit grayscale, values 0–15), but scaled_buf is created as MONO_HLSB and the scaling writes color 1. When blitted, the ‘on’ pixels will map to value 1, which is almost black on this display (making the eye very hard to see). Consider either scaling into a GS4_HMSB buffer and using color 15 for ‘on’ pixels, or providing a palette to blit() that maps 0→0 and 1→15.
| scaled_bitmap = bytearray((scaled_w * scaled_h) // 8) | |
| scaled_buf = framebuf.FrameBuffer( | |
| scaled_bitmap, scaled_w, scaled_h, framebuf.MONO_HLSB | |
| ) | |
| # Scale up pixel by pixel | |
| for y in range(EYE_H): | |
| for x in range(EYE_W): | |
| if buf.pixel(x, y): | |
| scaled_buf.fill_rect(x * SCALE, y * SCALE, SCALE, SCALE, 1) | |
| scaled_bitmap = bytearray((scaled_w * scaled_h) // 2) | |
| scaled_buf = framebuf.FrameBuffer( | |
| scaled_bitmap, scaled_w, scaled_h, framebuf.GS4_HMSB | |
| ) | |
| # Scale up pixel by pixel | |
| for y in range(EYE_H): | |
| for x in range(EYE_W): | |
| if buf.pixel(x, y): | |
| scaled_buf.fill_rect(x * SCALE, y * SCALE, SCALE, SCALE, 15) |
| scaled_w = EYE_W * SCALE | ||
| scaled_h = EYE_H * SCALE | ||
| scaled_bitmap = bytearray((scaled_w * scaled_h) // 8) | ||
| scaled_buf = framebuf.FrameBuffer( | ||
| scaled_bitmap, scaled_w, scaled_h, framebuf.MONO_HLSB | ||
| ) |
There was a problem hiding this comment.
draw_eye() allocates a new scaled_bitmap bytearray and FrameBuffer on every frame. For an animation loop this creates avoidable GC pressure and can cause stutters on constrained boards. Consider pre-scaling/caching the scaled framebuffers once (e.g., at module init) and only blitting in the hot loop, or reusing a single preallocated buffer with fill(0) each frame.
| # === Eye bitmaps (MONO_HLSB, 16x16, 2 bytes per row) === | ||
|
|
||
| EYE_OPEN = bytearray( | ||
| [ | ||
| 0b00000000, | ||
| 0b00000000, | ||
| 0b00111111, | ||
| 0b10000000, | ||
| 0b01000000, |
There was a problem hiding this comment.
The comment says the eye bitmaps are “16x16, 2 bytes per row”, which implies 32 bytes total for MONO_HLSB, but each bitmap literal below contains 33 bytes (based on the number of entries). This extra trailing byte is unused and can be confusing; consider trimming to 32 bytes or updating the dimensions/comment to match the actual data layout.
| """Blink animation example using framebuf blit and pixel scaling on SSD1327 OLED. | ||
|
|
||
| Displays a scaled 16x16 eye bitmap centered on the 128x128 round OLED screen | ||
| and animates a smooth blink sequence by cycling through five eye states. | ||
|
|
There was a problem hiding this comment.
PR description/issue checklist mentions updating lib/ssd1327/README.md examples table, but this PR only adds the example file. Please add blink_animation.py to the README Examples table (or update the PR description if that task is intentionally out of scope).
| x_offset = (128 - scaled_w) // 2 | ||
| y_offset = (128 - scaled_h) // 2 |
There was a problem hiding this comment.
This hard-codes 128 for centering. Since the display object already exposes display.width / display.height, using those would keep the example correct if the driver is instantiated with a different resolution (and avoids duplicating the docstring assumption).
Summary
Rapatriate and clean up the blink animation example from
micropython-steami-sample. Closes #407Changes
lib/ssd1327/examples/blink_animation.pybased onSCREEN/steami_animation/steami_animation.pysleepwithsleep_mstry/finallyto clear display on exitSCALE = 6for a 96x96px eye on the 128x128 round screenEYE_SQUINTintermediate state)@micropython.nativeondraw_eye()for faster renderingChecklist
ruff checkpassespython -m pytest tests/ -k mock -vpasseslib/ssd1327/examples/blink_animation.py)<scope>: <Description.>format